 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.osgi.framework.internal.core;

 import java.lang.reflect.Constructor ;
 import java.lang.reflect.InvocationTargetException ;
 import java.security.AccessController ;
 import java.security.PrivilegedAction ;
 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.Dictionary ;
 import java.util.Iterator ;

 import org.eclipse.osgi.framework.debug.Debug;
 import org.eclipse.osgi.framework.util.Headers;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;

 /**
  * RFC 1960-based Filter. Filter objects can be created by calling
  * the constructor with the desired filter string.
  * A Filter object can be called numerous times to determine if the
  * match argument matches the filter string that was used to create the Filter
  * object.
  *
  * <p>The syntax of a filter string is the string representation
  * of LDAP search filters as defined in RFC 1960:
  * <i>A String Representation of LDAP Search Filters</i> (available at
  * http://www.ietf.org/rfc/rfc1960.txt).
  * It should be noted that RFC 2254:
  * <i>A String Representation of LDAP Search Filters</i>
  * (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
  * RFC 1960 but only adds extensible matching and is not applicable for this
  * API.
  *
  * <p>The string representation of an LDAP search filter is defined by the
  * following grammar. It uses a prefix format.
  * <pre>
  * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
  * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
  * &lt;and&gt; ::= '&' &lt;filterlist&gt;
  * &lt;or&gt; ::= '|' &lt;filterlist&gt;
  * &lt;not&gt; ::= '!' &lt;filter&gt;
  * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
  * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
  * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
  * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
  * &lt;equal&gt; ::= '='
  * &lt;approx&gt; ::= '~='
  * &lt;greater&gt; ::= '&gt;='
  * &lt;less&gt; ::= '&lt;='
  * &lt;present&gt; ::= &lt;attr&gt; '=*'
  * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
  * &lt;initial&gt; ::= NULL | &lt;value&gt;
  * &lt;any&gt; ::= '*' &lt;starval&gt;
  * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
  * &lt;final&gt; ::= NULL | &lt;value&gt;
  * </pre>
  *
  * <code>&lt;attr&gt;</code> is a string representing an attribute, or
  * key, in the properties objects of the registered services.
  * Attribute names are not case sensitive;
  * that is cn and CN both refer to the same attribute.
  * <code>&lt;value&gt;</code> is a string representing the value, or part of
  * one, of a key in the properties objects of the registered services.
  * If a <code>&lt;value&gt;</code> must
  * contain one of the characters '<code>*</code>' or '<code>(</code>'
  * or '<code>)</code>', these characters
  * should be escaped by preceding them with the backslash '<code>\</code>'
  * character.
  * Note that although both the <code>&lt;substring&gt;</code> and
  * <code>&lt;present&gt;</code> productions can
  * produce the <code>'attr=*'</code> construct, this construct is used only to
  * denote a presence filter.
  *
  * <p>Examples of LDAP filters are:
  *
  * <pre>
  * &quot;(cn=Babs Jensen)&quot;
  * &quot;(!(cn=Tim Howes))&quot;
  * &quot;(&(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
  * &quot;(o=univ*of*mich*)&quot;
  * </pre>
  *
  * <p>The approximate match (<code>~=</code>) is implementation specific but
  * should at least ignore case and white space differences. Optional are
  * codes like soundex or other smart "closeness" comparisons.
  *
  * <p>Comparison of values is not straightforward. Strings
  * are compared differently than numbers and it is
  * possible for a key to have multiple values. Note that
  * that keys in the match argument must always be strings.
  * The comparison is defined by the object type of the key's
  * value. The following rules apply for comparison:
  *
  * <blockquote>
  * <TABLE BORDER=0>
  * <TR><TD><b>Property Value Type </b></TD><TD><b>Comparison Type</b></TD></TR>
  * <TR><TD>String </TD><TD>String comparison</TD></TR>
  * <TR valign=top><TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal </TD><TD>numerical comparison</TD></TR>
  * <TR><TD>Character </TD><TD>character comparison</TD></TR>
  * <TR><TD>Boolean </TD><TD>equality comparisons only</TD></TR>
  * <TR><TD>[] (array)</TD><TD>recursively applied to values </TD></TR>
  * <TR><TD>Vector</TD><TD>recursively applied to elements </TD></TR>
  * </TABLE>
  * Note: arrays of primitives are also supported.
  * </blockquote>
  *
  * A filter matches a key that has multiple values if it
  * matches at least one of those values. For example,
  * <pre>
  * Dictionary d = new Hashtable();
  * d.put( "cn", new String[] { "a", "b", "c" } );
  * </pre>
  * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
  *
  * <p>A filter component that references a key having an unrecognizable
  * data type will evaluate to <code>false</code> .
  */

 public class FilterImpl implements Filter /* since Framework 1.1 */{
     /* public methods in org.osgi.framework.Filter */

     /**
      * Constructs a {@link FilterImpl} object. This filter object may be used
      * to match a {@link ServiceReferenceImpl} or a Dictionary.
      *
      * <p> If the filter cannot be parsed, an {@link InvalidSyntaxException}
      * will be thrown with a human readable message where the
      * filter became unparsable.
      *
      * @param filter the filter string.
      * @exception InvalidSyntaxException If the filter parameter contains
      * an invalid filter string that cannot be parsed.
      */
     public FilterImpl(String filter) throws InvalidSyntaxException {
         topLevel = true;
         new Parser(filter).parse(this);
     }

     /**
      * Filter using a service's properties.
      * The Filter is executed using the referenced service's
      * properties.
      *
      * @param reference the reference to the service whose
      * properties are used in the match.
      * @return <code>true</code> if the service's properties match this filter;
      * <code>false</code> otherwise.
      */
     public boolean match(org.osgi.framework.ServiceReference reference) {
         return match0(((ServiceReferenceImpl) reference).registration.properties);
     }

     /**
      * Filter using a Dictionary.
      * The Filter is executed using the Dictionary's keys.
      *
      * @param dictionary the dictionary whose keys are used in the match.
      * @return <code>true</code> if the Dictionary's keys match this filter;
      * <code>false</code> otherwise.
      */
     public boolean match(Dictionary dictionary) {
         if (dictionary != null) {
             dictionary = new Headers(dictionary);
         }

         return match0(dictionary);
     }

     /**
      * Filter with case sensitivity using a <tt>Dictionary</tt> object. The
      * Filter is executed using the <tt>Dictionary</tt> object's keys and
      * values. The keys are case sensitivley matched with the filter.
      *
      * @param dictionary The <tt>Dictionary</tt> object whose keys are used in
      * the match.
      *
      * @return <tt>true</tt> if the <tt>Dictionary</tt> object's keys and
      * values match this filter; <tt>false</tt> otherwise.
      *
      * @since 1.3
      */
     public boolean matchCase(Dictionary dictionary) {
         return match0(dictionary);
     }

     /**
      * Returns this Filter object's filter string.
      * The filter string is normalized by removing
      * whitespace which does not affect the meaning of the filter.
      *
      * @return filter string.
      */
     public String toString() {
         if (this.filter == null) {
             StringBuffer filter = new StringBuffer ();
             filter.append('(');

             switch (operation) {
                 case AND :
                     {
                         filter.append('&');

                         FilterImpl[] filters = (FilterImpl[]) value;
                         int size = filters.length;

                         for (int i = 0; i < size; i++) {
                             filter.append(filters[i].toString());
                         }

                         break;
                     }

                 case OR :
                     {
                         filter.append('|');

                         FilterImpl[] filters = (FilterImpl[]) value;
                         int size = filters.length;

                         for (int i = 0; i < size; i++) {
                             filter.append(filters[i].toString());
                         }

                         break;
                     }

                 case NOT :
                     {
                         filter.append('!');
                         filter.append(value.toString());

                         break;
                     }

                 case SUBSTRING :
                     {
                         filter.append(attr);
                         filter.append('=');

                         String [] substrings = (String []) value;

                         int size = substrings.length;

                         for (int i = 0; i < size; i++) {
                             String substr = substrings[i];

                             if (substr == null) /* * */{
                                 filter.append('*');
                             } else /* xxx */{
                                 filter.append(encodeValue(substr));
                             }
                         }

                         break;
                     }
                 case EQUAL :
                     {
                         filter.append(attr);
                         filter.append('=');
                         filter.append(encodeValue(value.toString()));

                         break;
                     }
                 case GREATER :
                     {
                         filter.append(attr);
                         filter.append(">="); //$NON-NLS-1$
 filter.append(encodeValue(value.toString()));

                         break;
                     }
                 case LESS :
                     {
                         filter.append(attr);
                         filter.append("<="); //$NON-NLS-1$
 filter.append(encodeValue(value.toString()));

                         break;
                     }
                 case APPROX :
                     {
                         filter.append(attr);
                         filter.append("~="); //$NON-NLS-1$
 filter.append(encodeValue(approxString(value.toString())));

                         break;
                     }

                 case PRESENT :
                     {
                         filter.append(attr);
                         filter.append("=*"); //$NON-NLS-1$

                         break;
                     }
             }

             filter.append(')');

             if (topLevel) /* only hold onto String object at toplevel */{
                 this.filter = filter.toString();
             } else {
                 return filter.toString();
             }
         }

         return this.filter;
     }

     /**
      * Compares this Filter object to another object.
      *
      * @param obj the object to compare.
      * @return If the other object is a Filter object, then
      * returns <code>this.toString().equals(obj.toString())</code>,
      * otherwise <code>false</code>.
      */
     public boolean equals(Object obj) {
         if (obj == this) {
             return true;
         }

         if (!(obj instanceof FilterImpl)) {
             return false;
         }

         return this.toString().equals(obj.toString());
     }

     /**
      * Returns the hashCode for this Filter object.
      *
      * @return The hashCode of the filter string, <i>i.e.</i>
      * <code>this.toString().hashCode()</code>.
      */
     public int hashCode() {
         return this.toString().hashCode();
     }

     /* Protected fields and methods for the Filter implementation */

     /** filter operation */
     protected int operation;
     protected static final int EQUAL = 1;
     protected static final int APPROX = 2;
     protected static final int GREATER = 3;
     protected static final int LESS = 4;
     protected static final int PRESENT = 5;
     protected static final int SUBSTRING = 6;
     protected static final int AND = 7;
     protected static final int OR = 8;
     protected static final int NOT = 9;

     /** filter attribute or null if operation AND, OR or NOT */
     protected String attr;
     /** filter operands */
     protected Object value;

     /* normalized filter string for topLevel Filter object */
     protected String filter;

     /* true if root Filter object */
     protected boolean topLevel;

     protected FilterImpl() {
         topLevel = false;
     }

     protected void setFilter(int operation, String attr, Object value) {
         this.operation = operation;
         this.attr = attr;
         this.value = value;
     }

     /**
      * Filter using a service's properties.
      * The Filter is executed using the referenced service's
      * properties.
      *
      * @param reference A reference to the service whose
      * properties are used in the match.
      * @return If the service's properties match the filter,
      * return <code>true</code>. Otherwise, return <code>false</code>.
      */
     protected boolean match(ServiceReferenceImpl reference) {
         return match0(reference.registration.properties);
     }

     /**
      * Internal match routine.
      * Dictionary parameter must support case-insensitive get.
      *
      * @param properties A dictionary whose
      * keys are used in the match.
      * @return If the Dictionary's keys match the filter,
      * return <code>true</code>. Otherwise, return <code>false</code>.
      */
     protected boolean match0(Dictionary properties) {
         switch (operation) {
             case AND :
                 {
                     FilterImpl[] filters = (FilterImpl[]) value;
                     int size = filters.length;

                     for (int i = 0; i < size; i++) {
                         if (!filters[i].match0(properties)) {
                             return false;
                         }
                     }

                     return true;
                 }

             case OR :
                 {
                     FilterImpl[] filters = (FilterImpl[]) value;
                     int size = filters.length;

                     for (int i = 0; i < size; i++) {
                         if (filters[i].match0(properties)) {
                             return true;
                         }
                     }

                     return false;
                 }

             case NOT :
                 {
                     FilterImpl filter = (FilterImpl) value;

                     return !filter.match0(properties);
                 }

             case SUBSTRING :
             case EQUAL :
             case GREATER :
             case LESS :
             case APPROX :
                 {
                     Object prop = (properties == null) ? null : properties.get(attr);

                     return compare(operation, prop, value);
                 }

             case PRESENT :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("PRESENT(" + attr + ")"); //$NON-NLS-1$ //$NON-NLS-2$
 }

                     Object prop = (properties == null) ? null : properties.get(attr);

                     return prop != null;
                 }
         }

         return false;
     }

     /**
      * Encode the value string such that '(', '*', ')'
      * and '\' are escaped.
      *
      * @param value unencoded value string.
      * @return encoded value string.
      */
     protected static String encodeValue(String value) {
         boolean encoded = false;
         int inlen = value.length();
         int outlen = inlen << 1; /* inlen * 2 */

         char[] output = new char[outlen];
         value.getChars(0, inlen, output, inlen);

         int cursor = 0;
         for (int i = inlen; i < outlen; i++) {
             char c = output[i];

             switch (c) {
                 case '(' :
                 case '*' :
                 case ')' :
                 case '\\' :
                     {
                         output[cursor] = '\\';
                         cursor++;
                         encoded = true;

                         break;
                     }
             }

             output[cursor] = c;
             cursor++;
         }

         return encoded ? new String (output, 0, cursor) : value;
     }

     protected boolean compare(int operation, Object value1, Object value2) {
         if (value1 == null) {
             if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                 Debug.println("compare(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

             return false;
         }

         if (value1 instanceof String ) {
             return compare_String(operation, (String ) value1, value2);
         }

         Class clazz = value1.getClass();

         if (clazz.isArray()) {
             Class type = clazz.getComponentType();

             if (type.isPrimitive()) {
                 return compare_PrimitiveArray(operation, type, value1, value2);
             } else {
                 return compare_ObjectArray(operation, (Object []) value1, value2);
             }
         }

         if (value1 instanceof Collection ) {
             return compare_Collection(operation, (Collection ) value1, value2);
         }

         if (value1 instanceof Integer ) {
             return compare_Integer(operation, ((Integer ) value1).intValue(), value2);
         }

         if (value1 instanceof Long ) {
             return compare_Long(operation, ((Long ) value1).longValue(), value2);
         }

         if (value1 instanceof Byte ) {
             return compare_Byte(operation, ((Byte ) value1).byteValue(), value2);
         }

         if (value1 instanceof Short ) {
             return compare_Short(operation, ((Short ) value1).shortValue(), value2);
         }

         if (value1 instanceof Character ) {
             return compare_Character(operation, ((Character ) value1).charValue(), value2);
         }

         if (value1 instanceof Float ) {
             return compare_Float(operation, ((Float ) value1).floatValue(), value2);
         }

         if (value1 instanceof Double ) {
             return compare_Double(operation, ((Double ) value1).doubleValue(), value2);
         }

         if (value1 instanceof Boolean ) {
             return compare_Boolean(operation, ((Boolean ) value1).booleanValue(), value2);
         }

         if (value1 instanceof Comparable ) {
             return compare_Comparable(operation, (Comparable ) value1, value2);
         }
         
         return compare_Unknown(operation, value1, value2); // RFC 59
 }

     protected boolean compare_Collection(int operation, Collection collection, Object value2) {
         Iterator iterator = collection.iterator();

         while (iterator.hasNext()) {
             if (compare(operation, iterator.next(), value2)) {
                 return true;
             }
         }

         return false;
     }

     protected boolean compare_ObjectArray(int operation, Object [] array, Object value2) {
         int size = array.length;

         for (int i = 0; i < size; i++) {
             if (compare(operation, array[i], value2)) {
                 return true;
             }
         }

         return false;
     }

     protected boolean compare_PrimitiveArray(int operation, Class type, Object primarray, Object value2) {
         if (Integer.TYPE.isAssignableFrom(type)) {
             int[] array = (int[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Integer(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Long.TYPE.isAssignableFrom(type)) {
             long[] array = (long[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Long(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Byte.TYPE.isAssignableFrom(type)) {
             byte[] array = (byte[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Byte(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Short.TYPE.isAssignableFrom(type)) {
             short[] array = (short[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Short(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Character.TYPE.isAssignableFrom(type)) {
             char[] array = (char[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Character(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Float.TYPE.isAssignableFrom(type)) {
             float[] array = (float[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Float(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Double.TYPE.isAssignableFrom(type)) {
             double[] array = (double[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Double(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         if (Boolean.TYPE.isAssignableFrom(type)) {
             boolean[] array = (boolean[]) primarray;

             int size = array.length;

             for (int i = 0; i < size; i++) {
                 if (compare_Boolean(operation, array[i], value2)) {
                     return true;
                 }
             }

             return false;
         }

         return false;
     }

     protected boolean compare_String(int operation, String string, Object value2) {
         switch (operation) {
             case SUBSTRING :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("SUBSTRING(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
         
                     String [] substrings = (String []) value2;
                     int pos = 0;
         
                     int size = substrings.length;
         
                     for (int i = 0; i < size; i++) {
                         String substr = substrings[i];
         
                         if (i + 1 < size) /* if this is not that last substr */{
                             if (substr == null) /* * */{
                                 String substr2 = substrings[i + 1];
         
                                 if (substr2 == null) /* ** */
                                     continue; /* ignore first star */
                                 /* *xxx */
                                 if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                                     Debug.println("indexOf(\"" + substr2 + "\"," + pos + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                                 int index = string.indexOf(substr2, pos);
                                 if (index == -1) {
                                     return false;
                                 }
         
                                 pos = index + substr2.length();
                                 if (i + 2 < size) // if there are more substrings, increment over the string we just matched; otherwise need to do the last substr check
 i++;
                             } else /* xxx */{
                                 int len = substr.length();
         
                                 if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                                     Debug.println("regionMatches(" + pos + ",\"" + substr + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                                 if (string.regionMatches(pos, substr, 0, len)) {
                                     pos += len;
                                 } else {
                                     return false;
                                 }
                             }
                         } else /* last substr */{
                             if (substr == null) /* * */{
                                 return true;
                             } else /* xxx */{
                                 if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                                     Debug.println("regionMatches(" + pos + "," + substr + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                                 return string.endsWith(substr);
                             }
                         }
                     }
         
                     return true;
                 }
             case EQUAL :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("EQUAL(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return string.equals(value2);
                 }
             case APPROX :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("APPROX(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

                     string = approxString(string);
                     String string2 = approxString((String ) value2);

                     return string.equalsIgnoreCase(string2);
                 }
             case GREATER :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("GREATER(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return string.compareTo((String ) value2) >= 0;
                 }
             case LESS :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("LESS(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return string.compareTo((String ) value2) <= 0;
                 }
         }

         return false;
     }

     protected boolean compare_Integer(int operation, int intval, Object value2) {
         int intval2 = Integer.parseInt(((String ) value2).trim());

         switch (operation) {
             case SUBSTRING :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("SUBSTRING(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return false;
                 }
             case EQUAL :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("EQUAL(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return intval == intval2;
                 }
             case APPROX :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("APPROX(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return intval == intval2;
                 }
             case GREATER :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("GREATER(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return intval >= intval2;
                 }
             case LESS :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("LESS(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return intval <= intval2;
                 }
         }

         return false;
     }

     protected boolean compare_Long(int operation, long longval, Object value2) {
         long longval2 = Long.parseLong(((String ) value2).trim());

         switch (operation) {
             case SUBSTRING :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("SUBSTRING(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return false;
                 }
             case EQUAL :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("EQUAL(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return longval == longval2;
                 }
             case APPROX :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("APPROX(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return longval == longval2;
                 }
             case GREATER :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("GREATER(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return longval >= longval2;
                 }
             case LESS :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("LESS(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return longval <= longval2;
                 }
         }

         return false;
     }

     protected boolean compare_Byte(int operation, byte byteval, Object value2) {
         byte byteval2 = Byte.parseByte(((String ) value2).trim());

         switch (operation) {
             case SUBSTRING :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("SUBSTRING(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return false;
                 }
             case EQUAL :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("EQUAL(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return byteval == byteval2;
                 }
             case APPROX :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("APPROX(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return byteval == byteval2;
                 }
             case GREATER :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("GREATER(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return byteval >= byteval2;
                 }
             case LESS :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("LESS(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return byteval <= byteval2;
                 }
         }

         return false;
     }

     protected boolean compare_Short(int operation, short shortval, Object value2) {
         short shortval2 = Short.parseShort(((String ) value2).trim());

         switch (operation) {
             case SUBSTRING :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("SUBSTRING(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return false;
                 }
             case EQUAL :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("EQUAL(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return shortval == shortval2;
                 }
             case APPROX :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("APPROX(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return shortval == shortval2;
                 }
             case GREATER :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("GREATER(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return shortval >= shortval2;
                 }
             case LESS :
                 {
                     if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                         Debug.println("LESS(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                     return shortval <= shortval2;
                 }
         }

         return false;
     }

     protected boolean compare_Character(int operation, char charval, Object value2) {
         char charval2 = (((String ) value2).trim()).charAt(0);

         switch (operation) {
             case SUBSTRING :
                 {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return charval == charval2;
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return Character.toLowerCase(charval) == Character.toLowerCase(charval2);
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return charval >= charval2;
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return charval <= charval2;
                }
        }

        return false;
    }

    protected boolean compare_Boolean(int operation, boolean boolval, Object value2) {
        boolean boolval2 = new Boolean (((String ) value2).trim()).booleanValue();

        switch (operation) {
            case SUBSTRING :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return boolval == boolval2;
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return boolval == boolval2;
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return boolval == boolval2;
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return boolval == boolval2;
                }
        }

        return false;
    }

    protected boolean compare_Float(int operation, float floatval, Object value2) {
        float floatval2 = Float.parseFloat(((String ) value2).trim());

        switch (operation) {
            case SUBSTRING :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return floatval == floatval2;
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return floatval == floatval2;
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return floatval >= floatval2;
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return floatval <= floatval2;
                }
        }

        return false;
    }

    protected boolean compare_Double(int operation, double doubleval, Object value2) {
        double doubleval2 = Double.parseDouble(((String ) value2).trim());

        switch (operation) {
            case SUBSTRING :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return doubleval == doubleval2;
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return doubleval == doubleval2;
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return doubleval >= doubleval2;
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return doubleval <= doubleval2;
                }
        }

        return false;
    }
    
    protected static final Class [] constructorType = new Class [] {String .class};

    protected boolean compare_Comparable(int operation, Comparable value1, Object value2) {
        Constructor constructor;

        try {
            constructor = value1.getClass().getConstructor(constructorType);
        }
        catch (NoSuchMethodException e) {
            return false;
        }
        try {
            if (!constructor.isAccessible())
                AccessController.doPrivileged(new SetAccessibleAction(constructor));
            value2 = constructor.newInstance(new Object [] {((String ) value2).trim()});
        }
        catch (IllegalAccessException e) {
            return false;
        }
        catch (InvocationTargetException e) {
            return false;
        }
        catch (InstantiationException e) {
            return false;
        }

        switch (operation) {
            case SUBSTRING :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.compareTo(value2) == 0;
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.compareTo(value2) == 0;
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.compareTo(value2) >= 0;
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.compareTo(value2) <= 0;
                }
        }

        return false;
    }

    protected boolean compare_Unknown(int operation, Object value1, Object value2) { //RFC 59
 Constructor constructor;
        try {
            constructor = value1.getClass().getConstructor(constructorType);
        }
        catch (NoSuchMethodException e) {
            if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                Debug.println("Type not supported"); //$NON-NLS-1$
 }
            return false;
        }
        try {
            if (!constructor.isAccessible())
                AccessController.doPrivileged(new SetAccessibleAction(constructor));
            value2 = constructor.newInstance(new Object [] {((String ) value2).trim()});
        }
        catch (IllegalAccessException e) {
            return false;
        }
        catch (InvocationTargetException e) {
            return false;
        }
        catch (InstantiationException e) {
            return false;
        }
    
        switch (operation) {
            case SUBSTRING :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("SUBSTRING(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return false;
                }
            case EQUAL :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("EQUAL(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.equals(value2);
                }
            case APPROX :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("APPROX(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.equals(value2);
                }
            case GREATER :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("GREATER(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.equals(value2);
                }
            case LESS :
                {
                    if (Debug.DEBUG && Debug.DEBUG_FILTER) {
                        Debug.println("LESS(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                    return value1.equals(value2);
                }
        }
    
        return false;
    }

    /**
     * Map a string for an APPROX (~=) comparison.
     *
     * This implementation removes white spaces.
     * This is the minimum implementation allowed by
     * the OSGi spec.
     *
     * @param input Input string.
     * @return String ready for APPROX comparison.
     */
    protected static String approxString(String input) {
        boolean changed = false;
        char[] output = input.toCharArray();

        int length = output.length;

        int cursor = 0;
        for (int i = 0; i < length; i++) {
            char c = output[i];

            if (Character.isWhitespace(c)) {
                changed = true;
                continue;
            }

            output[cursor] = c;
            cursor++;
        }

        return changed ? new String (output, 0, cursor) : input;
    }
    
    /**
     * Returns the leftmost required objectClass value for the filter to evaluate to true.
     *
     * @return The leftmost required objectClass value or null if none could be determined.
     */
    public String getRequiredObjectClass() {
        // just checking for simple filters here where objectClass is the only attr or it is one attr of a base '&' clause
 // (objectClass=org.acme.BrickService) OK
 // (&(objectClass=org.acme.BrickService)(|(vendor=IBM)(vendor=SUN))) OK
 // (objectClass=org.acme.*) NOT OK
 // (|(objectClass=org.acme.BrickService)(objectClass=org.acme.CementService)) NOT OK
 // (&(objectClass=org.acme.BrickService)(objectClass=org.acme.CementService)) OK but only the first objectClass is returned
 switch (operation) {
            case EQUAL :
                if (attr.equalsIgnoreCase(org.osgi.framework.Constants.OBJECTCLASS) && (value instanceof String ))
                    return (String ) value;
                break;
            case AND :
                FilterImpl[] clauses = (FilterImpl[]) value;
                for (int i = 0; i < clauses.length; i++)
                    if (clauses[i].operation == EQUAL) {
                        String result = clauses[i].getRequiredObjectClass();
                        if (result != null)
                            return result;
                    }
                break;
        }
        return null;
    }

    /**
     * Returns a objectClass filter string for the specified objectClass.
     * @return A filter string for the specified objectClass or null if the specified objectClass is null.
     */
    public static String getObjectClassFilterString(String objectClass) {
        if (objectClass == null)
            return null;
        return "(" + org.osgi.framework.Constants.OBJECTCLASS + "=" + objectClass + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
 }

    /**
     * Returns all the attributes contained within this filter
     * @return all the attributes contained within this filter
     */
    public String [] getAttributes() {
        ArrayList results = new ArrayList ();
        getAttributesInternal(results);
        return (String []) results.toArray(new String [results.size()]);
    }

    private void getAttributesInternal(ArrayList results) {
        if (value instanceof FilterImpl[]) {
            FilterImpl[] children = (FilterImpl[]) value;
            for (int i = 0; i < children.length; i++)
                children[i].getAttributesInternal(results);
            return;
        } else if (value instanceof FilterImpl) {
            // The NOT operation only has one child filter (bug 188075)
 ((FilterImpl) value).getAttributesInternal(results);
            return;
        }
        if (attr != null)
            results.add(attr);
    }

    /**
     * Parser class for OSGi filter strings. This class parses
     * the complete filter string and builds a tree of Filter
     * objects rooted at the parent.
     */
    static class Parser {
        protected String filterstring;
        protected char[] filter;
        protected int pos;

        protected Parser(String filterstring) {
            this.filterstring = filterstring;
            filter = filterstring.toCharArray();
            pos = 0;
        }

        protected void parse(FilterImpl parent) throws InvalidSyntaxException {
            try {
                parse_filter(parent);
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new InvalidSyntaxException(Msg.FILTER_TERMINATED_ABRUBTLY, filterstring);
            }

            if (pos != filter.length) {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_TRAILING_CHARACTERS, String.valueOf(pos)), filterstring);
            }
        }

        protected void parse_filter(FilterImpl parent) throws InvalidSyntaxException {
            skipWhiteSpace();

            if (filter[pos] != '(') {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, String.valueOf(pos)), filterstring);
            }

            pos++;

            parse_filtercomp(parent);

            skipWhiteSpace();

            if (filter[pos] != ')') {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_RIGHTPAREN, String.valueOf(pos)), filterstring);
            }

            pos++;

            skipWhiteSpace();
        }

        protected void parse_filtercomp(FilterImpl parent) throws InvalidSyntaxException {
            skipWhiteSpace();

            char c = filter[pos];

            switch (c) {
                case '&' :
                    {
                        pos++;
                        parse_and(parent);
                        break;
                    }
                case '|' :
                    {
                        pos++;
                        parse_or(parent);
                        break;
                    }
                case '!' :
                    {
                        pos++;
                        parse_not(parent);
                        break;
                    }
                default :
                    {
                        parse_item(parent);
                        break;
                    }
            }
        }

        protected void parse_and(FilterImpl parent) throws InvalidSyntaxException {
            skipWhiteSpace();

            if (filter[pos] != '(') {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, String.valueOf(pos)), filterstring);
            }

            ArrayList operands = new ArrayList (10);

            while (filter[pos] == '(') {
                FilterImpl child = new FilterImpl();
                parse_filter(child);
                operands.add(child);
            }

            int size = operands.size();

            FilterImpl[] children = new FilterImpl[size];

            operands.toArray(children);

            parent.setFilter(FilterImpl.AND, null, children);
        }

        protected void parse_or(FilterImpl parent) throws InvalidSyntaxException {
            skipWhiteSpace();

            if (filter[pos] != '(') {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, String.valueOf(pos)), filterstring);
            }

            ArrayList operands = new ArrayList (10);

            while (filter[pos] == '(') {
                FilterImpl child = new FilterImpl();
                parse_filter(child);
                operands.add(child);
            }

            int size = operands.size();

            FilterImpl[] children = new FilterImpl[size];

            operands.toArray(children);

            parent.setFilter(FilterImpl.OR, null, children);
        }

        protected void parse_not(FilterImpl parent) throws InvalidSyntaxException {
            skipWhiteSpace();

            if (filter[pos] != '(') {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, String.valueOf(pos)), filterstring);
            }

            FilterImpl child = new FilterImpl();
            parse_filter(child);

            parent.setFilter(FilterImpl.NOT, null, child);
        }

        protected void parse_item(FilterImpl parent) throws InvalidSyntaxException {
            String attr = parse_attr();

            skipWhiteSpace();

            switch (filter[pos]) {
                case '~' :
                    {
                        if (filter[pos + 1] == '=') {
                            pos += 2;
                            parent.setFilter(FilterImpl.APPROX, attr, parse_value());
                            return;
                        }
                        break;
                    }
                case '>' :
                    {
                        if (filter[pos + 1] == '=') {
                            pos += 2;
                            parent.setFilter(FilterImpl.GREATER, attr, parse_value());
                            return;
                        }
                        break;
                    }
                case '<' :
                    {
                        if (filter[pos + 1] == '=') {
                            pos += 2;
                            parent.setFilter(FilterImpl.LESS, attr, parse_value());
                            return;
                        }
                        break;
                    }
                case '=' :
                    {
                        if (filter[pos + 1] == '*') {
                            int oldpos = pos;
                            pos += 2;
                            skipWhiteSpace();
                            if (filter[pos] == ')') {
                                parent.setFilter(FilterImpl.PRESENT, attr, null);
                                return; /* present */
                            }
                            pos = oldpos;
                        }

                        pos++;
                        Object string = parse_substring();

                        if (string instanceof String ) {
                            parent.setFilter(FilterImpl.EQUAL, attr, string);
                        } else {
                            parent.setFilter(FilterImpl.SUBSTRING, attr, string);
                        }

                        return;
                    }
            }

            throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_OPERATOR, String.valueOf(pos)), filterstring);
        }

        protected String parse_attr() throws InvalidSyntaxException {
            skipWhiteSpace();

            int begin = pos;
            int end = pos;

            char c = filter[pos];

            while ("~<>=()".indexOf(c) == -1) { //$NON-NLS-1$
 pos++;

                if (!Character.isWhitespace(c)) {
                    end = pos;
                }

                c = filter[pos];
            }

            int length = end - begin;

            if (length == 0) {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_ATTR, String.valueOf(pos)), filterstring);
            }

            return new String (filter, begin, length);
        }

        protected String parse_value() throws InvalidSyntaxException {
            StringBuffer sb = new StringBuffer (filter.length - pos);

            parseloop: while (true) {
                char c = filter[pos];

                switch (c) {
                    case ')' :
                        {
                            break parseloop;
                        }

                    case '(' :
                        {
                            throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, String.valueOf(pos)), filterstring);
                        }

                    case '\\' :
                        {
                            pos++;
                            c = filter[pos];
                            /* fall through into default */
                        }

                    default :
                        {
                            sb.append(c);
                            pos++;
                            break;
                        }
                }
            }

            if (sb.length() == 0) {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_VALUE, String.valueOf(pos)), filterstring);
            }

            return sb.toString();
        }

        protected Object parse_substring() throws InvalidSyntaxException {
            StringBuffer sb = new StringBuffer (filter.length - pos);

            ArrayList operands = new ArrayList (10);

            parseloop: while (true) {
                char c = filter[pos];

                switch (c) {
                    case ')' :
                        {
                            if (sb.length() > 0) {
                                operands.add(sb.toString());
                            }

                            break parseloop;
                        }

                    case '(' :
                        {
                            throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, String.valueOf(pos)), filterstring);
                        }

                    case '*' :
                        {
                            if (sb.length() > 0) {
                                operands.add(sb.toString());
                            }

                            sb.setLength(0);

                            operands.add(null);
                            pos++;

                            break;
                        }

                    case '\\' :
                        {
                            pos++;
                            c = filter[pos];
                            /* fall through into default */
                        }

                    default :
                        {
                            sb.append(c);
                            pos++;
                            break;
                        }
                }
            }

            int size = operands.size();

            if (size == 0) {
                throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_VALUE, String.valueOf(pos)), filterstring);
            }

            if (size == 1) {
                Object single = operands.get(0);

                if (single != null) {
                    return single;
                }
            }

            String [] strings = new String [size];

            operands.toArray(strings);

            return strings;
        }

        protected void skipWhiteSpace() {
            int length = filter.length;

            while ((pos < length) && Character.isWhitespace(filter[pos])) {
                pos++;
            }
        }
    }

    static class SetAccessibleAction implements PrivilegedAction {
        private Constructor constructor;
        public SetAccessibleAction(Constructor constructor) {
            this.constructor = constructor;
        }
        public Object run() {
            constructor.setAccessible(true);
            return null;
        }
    }
}

